package cn.js.filter;

import cn.js.domain.entity.LoginUser;
import cn.js.exception.CustomerAuthenticationException;
import cn.js.handler.LoginFailureHandler;
import cn.js.utils.JwtUtils;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * @Author Js
 * @Description
 * @Date 2024-11-02 22:01
 * @Version 1.0
 **/
//每一个servlet请求，只会执行一次
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            //获取当前请求的url地址
            String url = request.getRequestURI();
            //如果当前请求不是登录请求，则需要进行token验证
            if (!url.equals("/user/login")) {
                this.validateToken(request);
            }
        } catch (AuthenticationException e) {
            System.out.println(e);
            loginFailureHandler.onAuthenticationFailure(request, response, e);
        }
        //登录请求不需要验证token
        doFilter(request, response, filterChain);
    }

    /**
     * 验证token
     * <p>
     * 4.5 自定义认证失败处理器
     *
     * @param request
     */
    private void validateToken(HttpServletRequest request) throws AuthenticationException {
        //从头部获取token信息
        String token = request.getHeader("Authorization");
        //如果请求头部没有获取到token，则从请求的参数中进行获取
        if (ObjectUtils.isEmpty(token)) {
            token = request.getParameter("Authorization");
        }
        if (ObjectUtils.isEmpty(token)) {
            throw new CustomerAuthenticationException("token不存在");
        }

        //------- 判断redis中是否存在该token
        String tokenKey = "token_" + token;
        String redisToken = stringRedisTemplate.opsForValue().get(tokenKey);
        //如果redis里面没有token,说明该token失效
        if (ObjectUtils.isEmpty(redisToken)) {
            throw new CustomerAuthenticationException("token已过期");
        }



        //如果存在token，则从token中解析出用户名
        Claims claims = null;
        try {
            claims = JwtUtils.parseJWT(token);
        } catch (Exception e) {
            throw new CustomerAuthenticationException("token解析失败");
        }
        //获取到主题
        String loginUserString = claims.getSubject();
        //把字符串转成loginUser对象
        LoginUser loginUser = JSON.parseObject(loginUserString, LoginUser.class);
        //创建身份验证对象
        UsernamePasswordAuthenticationToken authenticationToken = new
                UsernamePasswordAuthenticationToken(loginUser, null,
                loginUser.getAuthorities());
        //设置到Spring Security上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}
